home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / msdos / lynx / source / www / library / implemen / htnews.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  30.5 KB  |  1,149 lines

  1. /*            NEWS ACCESS                HTNews.c
  2. **            ===========
  3. **
  4. ** History:
  5. **    26 Sep 90    Written TBL
  6. **    29 Nov 91    Downgraded to C, for portable implementation.
  7. */
  8. /* Implements:
  9. */
  10. #include"capalloc.h"
  11. #include "HTNews.h"
  12. #include"capstdio.h"
  13.  
  14. #define CR   FROMASCII('\015')    /* Must be converted to ^M for transmission */
  15. #define LF   FROMASCII('\012')    /* Must be converted to ^J for transmission */
  16.  
  17. #define NEWS_PORT 119        /* See rfc977 */
  18. #define APPEND            /* Use append methods */
  19. #define MAX_CHUNK    40    /* Largest number of articles in one window */
  20. #define CHUNK_SIZE    20    /* Number of articles for quick display */
  21.  
  22. #ifndef DEFAULT_NEWS_HOST
  23. #define DEFAULT_NEWS_HOST "news"
  24. #endif
  25. #ifndef SERVER_FILE
  26. #define SERVER_FILE "/usr/local/lib/rn/server"
  27. #endif
  28.  
  29. #include <ctype.h>
  30. #include "HTUtils.h"        /* Coding convention macros */
  31. #include "tcp.h"
  32.  
  33. #include "HTML.h"
  34. #include "HTParse.h"
  35. #include "HTFormat.h"
  36. #include "HTAlert.h"
  37.  
  38. #define BIG 1024 /* @@@ */
  39.  
  40. struct _HTStructured {
  41.     CONST HTStructuredClass *    isa;
  42.     /* ... */
  43. };
  44.  
  45. #define NEWS_PROGRESS(foo) HTProgress(foo)
  46.  
  47.  
  48. #define NEXT_CHAR HTGetChararcter()
  49. #define LINE_LENGTH 512            /* Maximum length of line of ARTICLE etc */
  50. #define GROUP_NAME_LENGTH    256    /* Maximum length of group name */
  51.  
  52.  
  53. /*    Module-wide variables
  54. */
  55. PUBLIC char * HTNewsHost;
  56. PRIVATE struct sockaddr_in soc_address;        /* Binary network address */
  57. PRIVATE int s;                    /* Socket for NewsHost */
  58. PRIVATE char response_text[LINE_LENGTH+1];    /* Last response */
  59. /* PRIVATE HText *    HT;    */        /* the new hypertext */
  60. PRIVATE HTStructured * target;            /* The output sink */
  61. PRIVATE HTStructuredClass targetClass;        /* Copy of fn addresses */
  62. PRIVATE HTParentAnchor *node_anchor;        /* Its anchor */
  63. PRIVATE int    diagnostic;            /* level: 0=none 2=source */
  64.  
  65.  
  66. #define PUTC(c) (*targetClass.put_character)(target, c)
  67. #define PUTS(s) (*targetClass.put_string)(target, s)
  68. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  69. #define END(e) (*targetClass.end_element)(target, e)
  70.  
  71. PUBLIC CONST char * HTGetNewsHost NOARGS
  72. {
  73.     return HTNewsHost;
  74. }
  75.  
  76. PUBLIC void HTSetNewsHost ARGS1(CONST char *, value)
  77. {
  78.     StrAllocCopy(HTNewsHost, value);
  79. }
  80.  
  81. /*    Initialisation for this module
  82. **    ------------------------------
  83. **
  84. **    Except on the NeXT, we pick up the NewsHost name from
  85. **
  86. **    1.    Environment variable NNTPSERVER
  87. **    2.    File SERVER_FILE
  88. **    3.    Compilation time macro DEFAULT_NEWS_HOST
  89. **    4.    Default to "news"
  90. */
  91. PRIVATE BOOL initialized = NO;
  92. PRIVATE BOOL initialize NOARGS
  93. {
  94.     CONST struct hostent  *phost;      /* Pointer to host - See netdb.h */
  95.     struct sockaddr_in* sin = &soc_address;
  96.     extern char *cp_nntphost;
  97.  
  98.  
  99. /*  Set up defaults:
  100. */
  101.     sin->sin_family = AF_INET;            /* Family = internet, host order  */
  102.     sin->sin_port = htons(NEWS_PORT);   /* Default: new port,    */
  103.  
  104. /*   Get name of Host
  105. */
  106.     StrAllocCopy(HTNewsHost, cp_nntphost);
  107. #ifndef RELEASE
  108.     if (TRACE) fprintf(stderr, "HTNews: NNTPSERVER defined as `%s'\n",
  109.         HTNewsHost);
  110. #endif /* RELEASE */
  111.  
  112.     if (*HTNewsHost>='0' && *HTNewsHost<='9') {   /* Numeric node address: */
  113.     sin->sin_addr.s_addr = inet_addr((char *)HTNewsHost); /* See arpa/inet.h */
  114.     } else {            /* Alphanumeric node name: */
  115.     phost=gethostbyname((char*)HTNewsHost);    /* See netdb.h */
  116.     if (!phost) {
  117.         char message[150];        /* @@@ */
  118.         sprintf(message,
  119.         "HTNews: Can't find news host `%s'.\n%s",HTNewsHost,
  120.         "Please define your NNTP server");
  121.         HTAlert(message);
  122. #ifndef RELEASE
  123.         CTRACE(tfp,
  124.           "HTNews: Can't find news host `%s'.\n",HTNewsHost);
  125. #endif /* RELEASE */
  126.         return NO;  /* Fail */
  127.     }
  128.     memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
  129.     }
  130.  
  131. #ifndef RELEASE
  132.     if (TRACE) fprintf(stderr,
  133.     "HTNews: Parsed address as port %4x, inet %d.%d.%d.%d\n",
  134.         (unsigned int)ntohs(sin->sin_port),
  135.         (int)*((unsigned char *)(&sin->sin_addr)+0),
  136.         (int)*((unsigned char *)(&sin->sin_addr)+1),
  137.         (int)*((unsigned char *)(&sin->sin_addr)+2),
  138.         (int)*((unsigned char *)(&sin->sin_addr)+3));
  139. #endif /* RELEASE */
  140.  
  141.     s = -1;        /* Disconnected */
  142.  
  143.     return YES;
  144. }
  145.  
  146.  
  147.  
  148. /*    Send NNTP Command line to remote host & Check Response
  149. **    ------------------------------------------------------
  150. **
  151. ** On entry,
  152. **    command    points to the command to be sent, including CRLF, or is null
  153. **        pointer if no command to be sent.
  154. ** On exit,
  155. **    Negative status indicates transmission error, socket closed.
  156. **    Positive status is an NNTP status.
  157. */
  158.  
  159.  
  160. PRIVATE int response ARGS1(CONST char *,command)
  161. {
  162.     int result;    
  163.     char * p = response_text;
  164.  
  165.     if (command) {
  166.         int status;
  167.     int length = strlen(command);
  168. #ifndef RELEASE
  169.     if (TRACE) fprintf(stderr, "NNTP command to be sent: %s", command);
  170. #endif /* RELEASE */
  171. #ifdef NOT_ASCII
  172.     {
  173.         CONST char  * p;
  174.         char     * q;
  175.         char ascii[LINE_LENGTH+1];
  176.         for(p = command, q=ascii; *p; p++, q++) {
  177.         *q = TOASCII(*p);
  178.         }
  179.             status = NETWRITE(s, ascii, length);
  180.     }
  181. #else
  182.         status = NETWRITE(s, command, length);
  183. #endif
  184.     if (status<0){
  185. #ifndef RELEASE
  186.         if (TRACE) fprintf(stderr,
  187.         "HTNews: Unable to send command. Disconnecting.\n");
  188. #endif /* RELEASE */
  189.         NETCLOSE(s);
  190.         s = -1;
  191.         return status;
  192.     } /* if bad status */
  193.     } /* if command to be sent */
  194.  
  195.     for(;;) {
  196.     if (((*p++=NEXT_CHAR) == LF)
  197.             || (p == &response_text[LINE_LENGTH])) {
  198.         *p++=0;                /* Terminate the string */
  199. #ifndef RELEASE
  200.         if (TRACE) fprintf(stderr, "NNTP Response: %s\n", response_text);
  201. #endif /* RELEASE */
  202.         sscanf(response_text, "%d", &result);
  203.         return result;        
  204.     } /* if end of line */
  205.     
  206.     if (*(p-1) < 0) {
  207. #ifndef RELEASE
  208.         if (TRACE) fprintf(stderr,
  209.         "HTNews: EOF on read, closing socket %d\n", s);
  210. #endif /* RELEASE */
  211.         NETCLOSE(s);    /* End of file, close socket */
  212.         return s = -1;    /* End of file on response */
  213.     }
  214.     } /* Loop over characters */
  215. }
  216.  
  217.  
  218. /*    Case insensitive string comparisons
  219. **    -----------------------------------
  220. **
  221. ** On entry,
  222. **    template must be already un upper case.
  223. **    unknown may be in upper or lower or mixed case to match.
  224. */
  225. PRIVATE BOOL match ARGS2 (CONST char *,unknown, CONST char *,template)
  226. {
  227.     CONST char * u = unknown;
  228.     CONST char * t = template;
  229.     for (;*u && *t && (TOUPPER(*u)==*t); u++, t++) /* Find mismatch or end */ ;
  230.     return (BOOL)(*t==0);        /* OK if end of template */
  231. }
  232.  
  233. /*    Find Author's name in mail address
  234. **    ----------------------------------
  235. **
  236. ** On exit,
  237. **    THE EMAIL ADDRESS IS CORRUPTED
  238. **
  239. ** For example, returns "Tim Berners-Lee" if given any of
  240. **    " Tim Berners-Lee <tim@online.cern.ch> "
  241. **  or    " tim@online.cern.ch ( Tim Berners-Lee ) "
  242. */
  243. PRIVATE char * author_name ARGS1 (char *,email)
  244. {
  245.     char *s, *e;
  246.     
  247.     if ((s=strchr(email,'(')) && (e=strchr(email, ')')))
  248.         if (e>s) {
  249.         *e=0;            /* Chop off everything after the ')'  */
  250.         return HTStrip(s+1);    /* Remove leading and trailing spaces */
  251.     }
  252.     
  253.     if ((s=strchr(email,'<')) && (e=strchr(email, '>')))
  254.         if (e>s) {
  255.         strcpy(s, e+1);        /* Remove <...> */
  256.         return HTStrip(email);    /* Remove leading and trailing spaces */
  257.     }
  258.     
  259.     return HTStrip(email);        /* Default to the whole thing */
  260.  
  261. }
  262.  
  263. /*    Start anchor element
  264. **    --------------------
  265. */
  266. PRIVATE void start_anchor ARGS1(CONST char *,  href)
  267. {
  268.     BOOL        present[HTML_A_ATTRIBUTES];
  269.     CONST char*        value[HTML_A_ATTRIBUTES];
  270.     
  271.     {
  272.         int i;
  273.         for(i=0; i<HTML_A_ATTRIBUTES; i++)
  274.         present[i] = (i==HTML_A_HREF);
  275.     }
  276.     value[HTML_A_HREF] = href;
  277. #ifdef MSDOSMEM
  278.     {
  279.         extern void *vp_msdosmem;
  280.         extern void **vpp_msdosmem;
  281.         vp_msdosmem = (void *)present;
  282.         vpp_msdosmem = (void **)value;
  283.     }
  284. #endif /* MSDOSMEM */
  285.     (*targetClass.start_element)(target, HTML_A , present, value);
  286.  
  287. }
  288.  
  289. /*    Paste in an Anchor
  290. **    ------------------
  291. **
  292. **
  293. ** On entry,
  294. **    HT     has a selection of zero length at the end.
  295. **    text     points to the text to be put into the file, 0 terminated.
  296. **    addr    points to the hypertext refernce address,
  297. **        terminated by white space, comma, NULL or '>' 
  298. */
  299. PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
  300. {
  301.     char href[LINE_LENGTH+1];
  302.         
  303.     {
  304.         CONST char * p;
  305.     strcpy(href,"news:");
  306.     for(p=addr; *p && (*p!='>') && !WHITE(*p) && (*p!=','); p++);
  307.         strncat(href, addr, p-addr);    /* Make complete hypertext reference */
  308.     }
  309.     
  310.     start_anchor(href);
  311.     PUTS(text);
  312.     END(HTML_A);
  313. }
  314.  
  315.  
  316. /*    Write list of anchors
  317. **    ---------------------
  318. **
  319. **    We take a pointer to a list of objects, and write out each,
  320. **    generating an anchor for each.
  321. **
  322. ** On entry,
  323. **    HT     has a selection of zero length at the end.
  324. **    text     points to a comma or space separated list of addresses.
  325. ** On exit,
  326. **    *text    is NOT any more chopped up into substrings.
  327. */
  328. PRIVATE void write_anchors ARGS1 (char *,text)
  329. {
  330.     char * start = text;
  331.     char * end;
  332.     char c;
  333.     for (;;) {
  334.         for(;*start && (WHITE(*start)); start++);  /* Find start */
  335.     if (!*start) return;            /* (Done) */
  336.         for(end=start; *end && (*end!=' ') && (*end!=','); end++);/* Find end */
  337.     if (*end) end++;    /* Include comma or space but not NULL */
  338.     c = *end;
  339.     *end = 0;
  340.     write_anchor(start, start);
  341.     *end = c;
  342.     start = end;            /* Point to next one */
  343.     }
  344. }
  345.  
  346. /*    Abort the connection                    abort_socket
  347. **    --------------------
  348. */
  349. PRIVATE void abort_socket NOARGS
  350. {
  351. #ifndef RELEASE
  352.     if (TRACE) fprintf(stderr,
  353.         "HTNews: EOF on read, closing socket %d\n", s);
  354. #endif /* RELEASE */
  355.     NETCLOSE(s);    /* End of file, close socket */
  356.     PUTS("Network Error: connection lost");
  357.     PUTC('\n');
  358.     s = -1;        /* End of file on response */
  359.     return;
  360. }
  361.  
  362. /*    Read in an Article                    read_article
  363. **    ------------------
  364. **
  365. **
  366. **    Note the termination condition of a single dot on a line by itself.
  367. **    RFC 977 specifies that the line "folding" of RFC850 is not used, so we
  368. **    do not handle it here.
  369. **
  370. ** On entry,
  371. **    s    Global socket number is OK
  372. **    HT    Global hypertext object is ready for appending text
  373. */       
  374. PRIVATE void read_article NOARGS
  375. {
  376.  
  377.     char line[LINE_LENGTH+1];
  378.     char *references=NULL;            /* Hrefs for other articles */
  379.     char *newsgroups=NULL;            /* Newsgroups list */
  380.     char *p = line;
  381.     BOOL done = NO;
  382.     
  383. /*    Read in the HEADer of the article:
  384. **
  385. **    The header fields are either ignored, or formatted and put into the
  386. **     Text.
  387. */
  388.     if (!diagnostic) {
  389. #ifdef MSDOSMEM
  390.     {
  391.         extern void *vp_msdosmem;
  392.         extern void **vpp_msdosmem;
  393.         vp_msdosmem = NULL;
  394.         vpp_msdosmem = NULL;
  395.     }
  396. #endif /* MSDOSMEM */
  397.     (*targetClass.start_element)(target, HTML_ADDRESS, 0, 0);
  398.     while(!done){
  399.         char ch = *p++ = NEXT_CHAR;
  400.         if (ch==(char)EOF) {
  401.         abort_socket();    /* End of file, close socket */
  402.             return;        /* End of file on response */
  403.         }
  404.         if ((ch == LF) || (p == &line[LINE_LENGTH])) {
  405.         *--p=0;                /* Terminate the string */
  406. #ifndef RELEASE
  407.         if (TRACE) fprintf(stderr, "H %s\n", line);
  408. #endif /* RELEASE */
  409.  
  410.         if (line[0]=='.') {    
  411.             if (line[1]<' ') {        /* End of article? */
  412.             done = YES;
  413.             break;
  414.             }
  415.         
  416.         } else if (line[0]<' ') {
  417.             break;        /* End of Header? */
  418.         } else if (match(line, "SUBJECT:")) {
  419.             END(HTML_ADDRESS);
  420. #ifdef MSDOSMEM
  421.     {
  422.         extern void *vp_msdosmem;
  423.         extern void **vpp_msdosmem;
  424.         vp_msdosmem = NULL;
  425.         vpp_msdosmem = NULL;
  426.     }
  427. #endif /* MSDOSMEM */
  428.             START(HTML_TITLE);            /** Uuugh! @@@ */
  429.             PUTS(line+8);
  430.             END(HTML_TITLE);
  431. #ifdef MSDOSMEM
  432.     {
  433.         extern void *vp_msdosmem;
  434.         extern void **vpp_msdosmem;
  435.         vp_msdosmem = NULL;
  436.         vpp_msdosmem = NULL;
  437.     }
  438. #endif /* MSDOSMEM */
  439.             START(HTML_ADDRESS);
  440. #ifdef MSDOSMEM
  441.     {
  442.         extern void *vp_msdosmem;
  443.         extern void **vpp_msdosmem;
  444.         vp_msdosmem = NULL;
  445.         vpp_msdosmem = NULL;
  446.     }
  447. #endif /* MSDOSMEM */
  448.             (*targetClass.start_element)(target, HTML_H1 , 0, 0);
  449.             PUTS(line+8);
  450.             (*targetClass.end_element)(target, HTML_H1);
  451. #ifdef MSDOSMEM
  452.     {
  453.         extern void *vp_msdosmem;
  454.         extern void **vpp_msdosmem;
  455.         vp_msdosmem = NULL;
  456.         vpp_msdosmem = NULL;
  457.     }
  458. #endif /* MSDOSMEM */
  459.             (*targetClass.start_element)(target, HTML_ADDRESS , 0, 0);
  460.         } else if (match(line, "DATE:")
  461.             || match(line, "FROM:")
  462.             || match(line, "ORGANIZATION:")) {
  463.             strcat(line, "\n");
  464.             PUTS(strchr(line,':')+1);
  465.         } else if (match(line, "NEWSGROUPS:")) {
  466.             StrAllocCopy(newsgroups, HTStrip(strchr(line,':')+1));
  467.             
  468.         } else if (match(line, "REFERENCES:")) {
  469.             StrAllocCopy(references, HTStrip(strchr(line,':')+1));
  470.             
  471.         } /* end if match */
  472.         p = line;            /* Restart at beginning */
  473.         } /* if end of line */
  474.     } /* Loop over characters */
  475.     (*targetClass.end_element)(target, HTML_ADDRESS);
  476.     
  477.     if (newsgroups || references) {
  478. #ifdef MSDOSMEM
  479.     {
  480.         extern void *vp_msdosmem;
  481.         extern void **vpp_msdosmem;
  482.         vp_msdosmem = NULL;
  483.         vpp_msdosmem = NULL;
  484.     }
  485. #endif /* MSDOSMEM */
  486.         (*targetClass.start_element)(target, HTML_DLC , 0, 0);
  487.         if (newsgroups) {
  488. #ifdef MSDOSMEM
  489.     {
  490.         extern void *vp_msdosmem;
  491.         extern void **vpp_msdosmem;
  492.         vp_msdosmem = NULL;
  493.         vpp_msdosmem = NULL;
  494.     }
  495. #endif /* MSDOSMEM */
  496.         (*targetClass.start_element)(target, HTML_DT , 0, 0);
  497.         PUTS("Newsgroups:");
  498. #ifdef MSDOSMEM
  499.     {
  500.         extern void *vp_msdosmem;
  501.         extern void **vpp_msdosmem;
  502.         vp_msdosmem = NULL;
  503.         vpp_msdosmem = NULL;
  504.     }
  505. #endif /* MSDOSMEM */
  506.         (*targetClass.start_element)(target, HTML_DD , 0, 0);
  507.         write_anchors(newsgroups);
  508.         free(newsgroups);
  509.         }
  510.         
  511.         if (references) {
  512. #ifdef MSDOSMEM
  513.     {
  514.         extern void *vp_msdosmem;
  515.         extern void **vpp_msdosmem;
  516.         vp_msdosmem = NULL;
  517.         vpp_msdosmem = NULL;
  518.     }
  519. #endif /* MSDOSMEM */
  520.         (*targetClass.start_element)(target, HTML_DT , 0, 0);
  521.         PUTS("References:");
  522. #ifdef MSDOSMEM
  523.     {
  524.         extern void *vp_msdosmem;
  525.         extern void **vpp_msdosmem;
  526.         vp_msdosmem = NULL;
  527.         vpp_msdosmem = NULL;
  528.     }
  529. #endif /* MSDOSMEM */
  530.         (*targetClass.start_element)(target, HTML_DD , 0, 0);
  531.         write_anchors(references);
  532.         free(references);
  533.         }
  534.         (*targetClass.end_element)(target, HTML_DLC);
  535.     }
  536.     PUTS("\n\n\n");
  537.     
  538.     }
  539.     
  540. /*    Read in the BODY of the Article:
  541. */
  542. #ifdef MSDOSMEM
  543.     {
  544.         extern void *vp_msdosmem;
  545.         extern void **vpp_msdosmem;
  546.         vp_msdosmem = NULL;
  547.         vpp_msdosmem = NULL;
  548.     }
  549. #endif /* MSDOSMEM */
  550.     (*targetClass.start_element)(target, HTML_PRE , 0, 0);
  551.  
  552.     p = line;
  553.     while(!done){
  554.     char ch = *p++ = NEXT_CHAR;
  555.     if (ch==(char)EOF) {
  556.         abort_socket();    /* End of file, close socket */
  557.         return;        /* End of file on response */
  558.     }
  559.     if ((ch == LF) || (p == &line[LINE_LENGTH])) {
  560.         *p++=0;                /* Terminate the string */
  561. #ifndef RELEASE
  562.         if (TRACE) fprintf(stderr, "B %s", line);
  563. #endif /* RELEASE */
  564.         if (line[0]=='.') {
  565.         if (line[1]<' ') {        /* End of article? */
  566.             done = YES;
  567.             break;
  568.         } else {            /* Line starts with dot */
  569.             PUTS(&line[1]);    /* Ignore first dot */
  570.         }
  571.         } else {
  572.  
  573. /*    Normal lines are scanned for buried references to other articles.
  574. **    Unfortunately, it will pick up mail addresses as well!
  575. */
  576.         char *l = line;
  577.         char * p;
  578.         while (p=strchr(l, '<')) {
  579.             char *q  = strchr(p,'>');
  580.             char *at = strchr(p, '@');
  581.             if (q && at && at<q) {
  582.                 char c = q[1];
  583.             q[1] = 0;        /* chop up */
  584.             *p = 0;
  585.             PUTS(l);
  586.             *p = '<';         /* again */
  587.             *q = 0;
  588.             start_anchor(p+1);
  589.             *q = '>';         /* again */
  590.             PUTS(p);
  591.             (*targetClass.end_element)(target, HTML_A);
  592.             q[1] = c;        /* again */
  593.             l=q+1;
  594.             } else break;        /* line has unmatched <> */
  595.         } 
  596.         PUTS( l);    /* Last bit of the line */
  597.         } /* if not dot */
  598.         p = line;                /* Restart at beginning */
  599.     } /* if end of line */
  600.     } /* Loop over characters */
  601.     
  602.     (*targetClass.end_element)(target, HTML_PRE);
  603. }
  604.  
  605.  
  606. /*    Read in a List of Newsgroups
  607. **    ----------------------------
  608. */
  609. /*
  610. **    Note the termination condition of a single dot on a line by itself.
  611. **    RFC 977 specifies that the line "folding" of RFC850 is not used, so we
  612. **    do not handle it here.
  613. */        
  614. PRIVATE void read_list NOARGS
  615. {
  616.  
  617.     char line[LINE_LENGTH+1];
  618.     char *p;
  619.     BOOL done = NO;
  620.     
  621. /*    Read in the HEADer of the article:
  622. **
  623. **    The header fields are either ignored, or formatted and put into the
  624. **    Text.
  625. */
  626. #ifdef MSDOSMEM
  627.     {
  628.         extern void *vp_msdosmem;
  629.         extern void **vpp_msdosmem;
  630.         vp_msdosmem = NULL;
  631.         vpp_msdosmem = NULL;
  632.     }
  633. #endif /* MSDOSMEM */
  634.     (*targetClass.start_element)(target, HTML_H1 , 0, 0);
  635.     PUTS( "Newsgroups");
  636.     (*targetClass.end_element)(target, HTML_PRE);
  637.     p = line;
  638. #ifdef MSDOSMEM
  639.     {
  640.         extern void *vp_msdosmem;
  641.         extern void **vpp_msdosmem;
  642.         vp_msdosmem = NULL;
  643.         vpp_msdosmem = NULL;
  644.     }
  645. #endif /* MSDOSMEM */
  646.     (*targetClass.start_element)(target, HTML_MENU , 0, 0);
  647.     while(!done){
  648.     char ch = *p++ = NEXT_CHAR;
  649.     if (ch==(char)EOF) {
  650.         abort_socket();    /* End of file, close socket */
  651.         return;        /* End of file on response */
  652.     }
  653.     if ((ch == LF) || (p == &line[LINE_LENGTH])) {
  654.         *p++=0;                /* Terminate the string */
  655. #ifndef RELEASE
  656.         if (TRACE) fprintf(stderr, "B %s", line);
  657. #endif /* RELEASE */
  658. #ifdef MSDOSMEM
  659.     {
  660.         extern void *vp_msdosmem;
  661.         extern void **vpp_msdosmem;
  662.         vp_msdosmem = NULL;
  663.         vpp_msdosmem = NULL;
  664.     }
  665. #endif /* MSDOSMEM */
  666.         (*targetClass.start_element)(target, HTML_LI , 0, 0);
  667.         if (line[0]=='.') {
  668.         if (line[1]<' ') {        /* End of article? */
  669.             done = YES;
  670.             break;
  671.         } else {            /* Line starts with dot */
  672.             PUTS( &line[1]);
  673.         }
  674.         } else {
  675.  
  676. /*    Normal lines are scanned for references to newsgroups.
  677. */
  678.         char group[LINE_LENGTH];
  679.         int first, last;
  680.         char postable;
  681.         if (sscanf(line, "%s %d %d %c", group, &first, &last, &postable)==4)
  682.             write_anchor(line, group);
  683.         else
  684.             PUTS(line);
  685.         } /* if not dot */
  686.         p = line;            /* Restart at beginning */
  687.     } /* if end of line */
  688.     } /* Loop over characters */
  689.     (*targetClass.end_element)(target, HTML_MENU);
  690. }
  691.  
  692.  
  693. /*    Read in a Newsgroup
  694. **    -------------------
  695. **    Unfortunately, we have to ask for each article one by one if we
  696. **    want more than one field.
  697. **
  698. */
  699. PRIVATE void read_group ARGS3(
  700.   CONST char *,groupName,
  701.   int,first_required,
  702.   int,last_required
  703. )
  704. {
  705.     char line[LINE_LENGTH+1];
  706.     char author[LINE_LENGTH+1];
  707.     char subject[LINE_LENGTH+1];
  708.     char *p;
  709.     BOOL done;
  710.  
  711.     char buffer[LINE_LENGTH];
  712.     char *reference=0;            /* Href for article */
  713.     int art;                /* Article number WITHIN GROUP */
  714.     int status, count, first, last;    /* Response fields */
  715.                     /* count is only an upper limit */
  716.  
  717.     sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
  718. #ifndef RELEASE
  719.     if(TRACE) printf("Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",
  720.             status, count, first, last, first_required, last_required);
  721. #endif /* RELEASE */
  722.     if (last==0) {
  723.         PUTS( "\nNo articles in this group.\n");
  724.     return;
  725.     }
  726.     
  727. #define FAST_THRESHOLD 100    /* Above this, read IDs fast */
  728. #define CHOP_THRESHOLD 50    /* Above this, chop off the rest */
  729.  
  730.     if (first_required<first) first_required = first;        /* clip */
  731.     if ((last_required==0) || (last_required > last)) last_required = last;
  732.     
  733.     if (last_required<=first_required) {
  734.         PUTS( "\nNo articles in this range.\n");
  735.     return;
  736.     }
  737.  
  738.     if (last_required-first_required+1 > MAX_CHUNK) {    /* Trim this block */
  739.         first_required = last_required-CHUNK_SIZE+1;
  740.     }
  741. #ifndef RELEASE
  742.     if (TRACE) printf (
  743.     "    Chunk will be (%d-%d)\n", first_required, last_required);
  744. #endif /* RELEASE */
  745.  
  746. /*    Set window title
  747. */
  748.     sprintf(buffer, "Newsgroup %s,  Articles %d-%d",
  749.             groupName, first_required, last_required);
  750. #ifdef MSDOSMEM
  751.     {
  752.         extern void *vp_msdosmem;
  753.         extern void **vpp_msdosmem;
  754.         vp_msdosmem = NULL;
  755.         vpp_msdosmem = NULL;
  756.     }
  757. #endif /* MSDOSMEM */
  758.     START(HTML_TITLE);
  759.     PUTS(buffer);
  760.     END(HTML_TITLE);
  761.  
  762. /*    Link to earlier articles
  763. */
  764.     if (first_required>first) {
  765.         int before;            /* Start of one before */
  766.     if (first_required-MAX_CHUNK <= first) before = first;
  767.     else before = first_required-CHUNK_SIZE;
  768.         sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1);
  769. #ifndef RELEASE
  770.     if (TRACE) fprintf(stderr, "    Block before is %s\n", buffer);
  771. #endif /* RELEASE */
  772.     PUTS( " (");
  773.     start_anchor(buffer);
  774.     PUTS("Earlier articles");
  775.     END(HTML_A);
  776.     PUTS( "...)\n");
  777.     }
  778.     
  779.     done = NO;
  780.  
  781. /*#define USE_XHDR*/
  782. #ifdef USE_XHDR
  783.     if (count>FAST_THRESHOLD)  {
  784.         sprintf(buffer,
  785.     "\nThere are about %d articles currently available in %s, IDs as follows:\n\n",
  786.         count, groupName); 
  787.         PUTS(buffer);
  788.         sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF);
  789.     status = response(buffer);
  790.     if (status==221) {
  791.  
  792.         p = line;
  793.         while(!done){
  794.         char ch = *p++ = NEXT_CHAR;
  795.         if (ch==(char)EOF) {
  796.             abort_socket();    /* End of file, close socket */
  797.             return;        /* End of file on response */
  798.         }
  799.         if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
  800.             *p++=0;                /* Terminate the string */
  801. #ifndef RELEASE
  802.             if (TRACE) fprintf(stderr, "X %s", line);
  803. #endif /* RELEASE */
  804.             if (line[0]=='.') {
  805.             if (line[1]<' ') {        /* End of article? */
  806.                 done = YES;
  807.                 break;
  808.             } else {            /* Line starts with dot */
  809.                     /* Ignore strange line */
  810.             }
  811.             } else {
  812.     
  813.     /*    Normal lines are scanned for references to articles.
  814.     */
  815.             char * space = strchr(line, ' ');
  816.             if (space++)
  817.                 write_anchor(space, space);
  818.             } /* if not dot */
  819.             p = line;            /* Restart at beginning */
  820.         } /* if end of line */
  821.         } /* Loop over characters */
  822.  
  823.         /* leaving loop with "done" set */
  824.     } /* Good status */
  825.     };
  826. #endif
  827.  
  828. /*    Read newsgroup using individual fields:
  829. */
  830.     if (!done) {
  831.         if (first==first_required && last==last_required)
  832.         PUTS("\nAll available articles in ");
  833.         else PUTS( "\nArticles in ");
  834.     PUTS(groupName);
  835. #ifdef MSDOSMEM
  836.     {
  837.         extern void *vp_msdosmem;
  838.         extern void **vpp_msdosmem;
  839.         vp_msdosmem = NULL;
  840.         vpp_msdosmem = NULL;
  841.     }
  842. #endif /* MSDOSMEM */
  843.     START(HTML_MENU);
  844.     for(art=first_required; art<=last_required; art++) {
  845.     
  846. /*#define OVERLAP*/
  847. #ifdef OVERLAP
  848. /* With this code we try to keep the server running flat out by queuing just
  849. ** one extra command ahead of time. We assume (1) that the server won't abort
  850. ** if it gets input during output, and (2) that TCP buffering is enough for the
  851. ** two commands. Both these assumptions seem very reasonable. However, we HAVE
  852. ** had a hangup with a loaded server.
  853. */
  854.         if (art==first_required) {
  855.         if (art==last_required) {
  856.             sprintf(buffer, "HEAD %d%c%c", art, CR, LF);    /* Only one */
  857.             status = response(buffer);
  858.             } else {                    /* First of many */
  859.             sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c",
  860.                 art, CR, LF, art+1, CR, LF);
  861.             status = response(buffer);
  862.             }
  863.         } else if (art==last_required) {            /* Last of many */
  864.             status = response(NULL);
  865.         } else {                        /* Middle of many */
  866.             sprintf(buffer, "HEAD %d%c%c", art+1, CR, LF);
  867.             status = response(buffer);
  868.         }
  869.         
  870. #else    /* NOT OVERLAP */
  871.         sprintf(buffer, "HEAD %d%c%c", art, CR, LF);
  872.         status = response(buffer);
  873. #endif    /* NOT OVERLAP */
  874.  
  875.         if (status == 221) {    /* Head follows - parse it:*/
  876.     
  877.         p = line;                /* Write pointer */
  878.         done = NO;
  879.         while(!done){
  880.             char ch = *p++ = NEXT_CHAR;
  881.             if (ch==(char)EOF) {
  882.             abort_socket();    /* End of file, close socket */
  883.             return;        /* End of file on response */
  884.             }
  885.             if ((ch == LF)
  886.             || (p == &line[LINE_LENGTH]) ) {
  887.             
  888.             *--p=0;        /* Terminate  & chop LF*/
  889.             p = line;        /* Restart at beginning */
  890. #ifndef RELEASE
  891.             if (TRACE) fprintf(stderr, "G %s\n", line);
  892. #endif /* RELEASE */
  893.             switch(line[0]) {
  894.     
  895.             case '.':
  896.                 done = (line[1]<' ');    /* End of article? */
  897.                 break;
  898.     
  899.             case 'S':
  900.             case 's':
  901.                 if (match(line, "SUBJECT:"))
  902.                 strcpy(subject, line+9);/* Save subject */
  903.                 break;
  904.     
  905.             case 'M':
  906.             case 'm':
  907.                 if (match(line, "MESSAGE-ID:")) {
  908.                 char * addr = HTStrip(line+11) +1; /* Chop < */
  909.                 addr[strlen(addr)-1]=0;        /* Chop > */
  910.                 StrAllocCopy(reference, addr);
  911.                 }
  912.                 break;
  913.     
  914.             case 'f':
  915.             case 'F':
  916.                 if (match(line, "FROM:")) {
  917.                 char * p;
  918.                 strcpy(author,
  919.                     author_name(strchr(line,':')+1));
  920.                 p = author + strlen(author) - 1;
  921.                 if (*p==LF) *p = 0;    /* Chop off newline */
  922.                 }
  923.                 break;
  924.                     
  925.             } /* end switch on first character */
  926.             } /* if end of line */
  927.         } /* Loop over characters */
  928.     
  929. #ifdef MSDOSMEM
  930.     {
  931.         extern void *vp_msdosmem;
  932.         extern void **vpp_msdosmem;
  933.         vp_msdosmem = NULL;
  934.         vpp_msdosmem = NULL;
  935.     }
  936. #endif /* MSDOSMEM */
  937.         START(HTML_LI);
  938.         sprintf(buffer, "\"%s\" - %s", subject, author);
  939.         if (reference) {
  940.             write_anchor(buffer, reference);
  941.             free(reference);
  942.             reference=0;
  943.         } else {
  944.             PUTS(buffer);
  945.         }
  946.         
  947.     
  948. /*     indicate progress!   @@@@@@
  949. */
  950.     
  951.         } /* If good response */
  952.     } /* Loop over article */        
  953.     } /* If read headers */
  954.     END(HTML_MENU);
  955. #ifdef MSDOSMEM
  956.     {
  957.         extern void *vp_msdosmem;
  958.         extern void **vpp_msdosmem;
  959.         vp_msdosmem = NULL;
  960.         vpp_msdosmem = NULL;
  961.     }
  962. #endif /* MSDOSMEM */
  963.     START(HTML_P);
  964.     
  965. /*    Link to later articles
  966. */
  967.     if (last_required<last) {
  968.         int after;            /* End of article after */
  969.     after = last_required+CHUNK_SIZE;
  970.         if (after==last) sprintf(buffer, "news:%s", groupName);    /* original group */
  971.         else sprintf(buffer, "news:%s/%d-%d", groupName, last_required+1, after);
  972. #ifndef RELEASE
  973.     if (TRACE) fprintf(stderr, "    Block after is %s\n", buffer);
  974. #endif /* RELEASE */
  975.     PUTS( "(");
  976.     start_anchor(buffer);
  977.     PUTS( "Later articles");
  978.     END(HTML_A);
  979.     PUTS( "...)\n");
  980.     }
  981.     
  982.  
  983. }
  984.  
  985.  
  986. /*        Load by name                    HTLoadNews
  987. **        ============
  988. */
  989. PUBLIC int HTLoadNews ARGS4(
  990.     CONST char *,        arg,
  991.     HTParentAnchor *,    anAnchor,
  992.     HTFormat,        format_out,
  993.     HTStream*,        stream)
  994. {
  995.     char command[257];            /* The whole command */
  996.     char groupName[GROUP_NAME_LENGTH];    /* Just the group name */
  997.     int status;                /* tcp return */
  998.     int retries;            /* A count of how hard we have tried */
  999.     BOOL group_wanted;            /* Flag: group was asked for, not article */
  1000.     BOOL list_wanted;            /* Flag: group was asked for, not article */
  1001.     int first, last;            /* First and last articles asked for */
  1002.  
  1003.     diagnostic = (format_out == WWW_SOURCE);    /* set global flag */
  1004.     
  1005. #ifndef RELEASE
  1006.     if (TRACE) fprintf(stderr, "HTNews: Looking for %s\n", arg);
  1007. #endif /* RELEASE */
  1008.     if (!initialized) initialized = initialize();
  1009.     if (!initialized) return -1;    /* FAIL */
  1010.     
  1011.     {
  1012.         CONST char * p1=arg;
  1013.  
  1014. /*    We will ask for the document, omitting the host name & anchor.
  1015. **
  1016. **    Syntax of address is
  1017. **        xxx@yyy            Article
  1018. **        <xxx@yyy>        Same article
  1019. **        xxxxx            News group (no "@")
  1020. **        group/n1-n2        Articles n1 to n2 in group
  1021. */        
  1022.     group_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')==0);
  1023.     list_wanted  = (strchr(arg, '@')==0) && (strchr(arg, '*')!=0);
  1024.  
  1025.     /* p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION); */
  1026.     /* Don't use HTParse because news: access doesn't follow traditional
  1027.        rules. For instance, if the article reference contains a '#',
  1028.        the rest of it is lost -- JFG 10/7/92, from a bug report */
  1029.      if (!strncasecomp (arg, "news:", 5))
  1030.       p1 = arg + 5;  /* Skip "news:" prefix */
  1031.     if (list_wanted) {
  1032.         strcpy(command, "LIST ");
  1033.     } else if (group_wanted) {
  1034.         char * slash = strchr(p1, '/');
  1035.         strcpy(command, "GROUP ");
  1036.         first = 0;
  1037.         last = 0;
  1038.         if (slash) {
  1039.         *slash = 0;
  1040.         strcpy(groupName, p1);
  1041.         *slash = '/';
  1042.         (void) sscanf(slash+1, "%d-%d", &first, &last);
  1043.         } else {
  1044.         strcpy(groupName, p1);
  1045.         }
  1046.         strcat(command, groupName);
  1047.     } else {
  1048.         strcpy(command, "ARTICLE ");
  1049.         if (strchr(p1, '<')==0) strcat(command,"<");
  1050.         strcat(command, p1);
  1051.         if (strchr(p1, '>')==0) strcat(command,">");
  1052.     }
  1053.  
  1054.         {
  1055.         char * p = command + strlen(command);
  1056.         *p++ = CR;        /* Macros to be correct on Mac */
  1057.         *p++ = LF;
  1058.         *p++ = 0;
  1059.         /* strcat(command, "\r\n");    */    /* CR LF, as in rfc 977 */
  1060.     }
  1061.     } /* scope of p1 */
  1062.     
  1063.     if (!*arg) return NO;            /* Ignore if no name */
  1064.  
  1065.  
  1066. /*    Make a hypertext object with an anchor list.
  1067. */       
  1068.     node_anchor = anAnchor;
  1069.     target = HTML_new(anAnchor, format_out, stream);
  1070.     targetClass = *target->isa;    /* Copy routine entry points */
  1071.     
  1072.         
  1073. /*    Now, let's get a stream setup up from the NewsHost:
  1074. */       
  1075.     for(retries=0;retries<2; retries++){
  1076.     
  1077.         if (s<0) {
  1078.             NEWS_PROGRESS("Connecting to NewsHost ...");
  1079.         s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  1080.         status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
  1081.         if (status<0){
  1082.         char message[256];
  1083.             NETCLOSE(s);
  1084.         s = -1;
  1085. #ifndef RELEASE
  1086.         if (TRACE) fprintf(stderr, "HTNews: Unable to connect to news host.\n");
  1087. #endif /* RELEASE */
  1088. /*        if (retries<=1) continue;   WHY TRY AGAIN ?     */
  1089.         sprintf(message,
  1090. "\nCould not access %s.\n\n (Check default WorldWideWeb NewsHost ?)\n",
  1091.             HTNewsHost);
  1092.         return HTLoadError(stream, 500, message);
  1093.         } else {
  1094. #ifndef RELEASE
  1095.         if (TRACE) fprintf(stderr, "HTNews: Connected to news host %s.\n",
  1096.                 HTNewsHost);
  1097. #endif /* RELEASE */
  1098.         HTInitInput(s);        /* set up buffering */
  1099.         if ((response(NULL) / 100) !=2) {
  1100.             char message[BIG];
  1101.             NETCLOSE(s);
  1102.             s = -1;
  1103.             sprintf(message, 
  1104.           "Can't read news info. News host %.20s responded: %.200s",
  1105.                 HTNewsHost, response_text);
  1106.                 return HTLoadError(stream, 500, message);
  1107.         }
  1108.         }
  1109.     } /* If needed opening */
  1110.     
  1111.     /* @@@@@@@@@@@@@@Tell user something's happening */
  1112.     
  1113.     status = response(command);
  1114.     if (status<0) break;
  1115.     if ((status/ 100) !=2) {
  1116.         HTProgress(response_text);
  1117. /*        NXRunAlertPanel("News access", response_text,
  1118.             NULL,NULL,NULL);
  1119. */
  1120.         NETCLOSE(s);
  1121.         s = -1;
  1122. /* return HT; -- no:the message might be "Timeout-disconnected" left over */
  1123.         continue;    /*    Try again */
  1124.     }
  1125.   
  1126. /*    Load a group, article, etc
  1127. */
  1128.         
  1129.     
  1130.     if (list_wanted) read_list();
  1131.     else if (group_wanted) read_group(groupName, first, last);
  1132.         else read_article();
  1133.  
  1134.     (*targetClass.free)(target);
  1135.     return HT_LOADED;
  1136.     
  1137.     } /* Retry loop */
  1138.     
  1139.     
  1140.     /* HTAlert("Sorry, could not load requested news.\n"); */
  1141.         
  1142. /*    NXRunAlertPanel(NULL, "Sorry, could not load `%s'.",
  1143.         NULL,NULL,NULL, arg);No -- message earlier wil have covered it */
  1144.  
  1145.     return HT_LOADED;
  1146. }
  1147.  
  1148. GLOBALDEF PUBLIC HTProtocol HTNews = { "news", HTLoadNews, NULL };
  1149.